#include "Core.h"
#include "Sheet.h"
#include "Filesystem.h"
#include "Logger.h"
#include "simpleimageluabindings.h"

const std::string Core::DEFAULT_PATH = "core.Core";
const int Core::CHAN_ANY = -1;
const int Core::SHEET_GLOBAL = -1;

Core::Core()
{
	init("", 0, NULL);
}

Core::Core(std::string localPath)
{
	init(localPath, 0, NULL);
}

Core::Core(int argc, char** argv)
{
	// Get local path from executable (remove filename from end)
	std::string execName(argv[0]);
	size_t pathEnd = execName.find_last_of( FS_PATH_SEPARATOR );

	// If there is no trailing slash, ignore the input (it is just a filename)
	if (pathEnd != std::string::npos) {
		std::string path = execName.substr(0, pathEnd);
		init(path, argc, argv);
	} else {
		init("", argc, argv);
	}

	// parse system arguments
	std::vector<std::string> curArgVals;
	std::string curArgName = "NO_CONTEXT";
	for(int i = 1; i < argc; ++i)
	{
		std::string tempVal(argv[i]);
		char fchar = argv[i][0];
		if(fchar == '-' || fchar == '+')
		{
			_sysargs[curArgName] = curArgVals;
			_sysargc.push_back(curArgName);
			curArgVals.clear();
			curArgName = tempVal;
		}
		else
			curArgVals.push_back(tempVal);
	}
	_sysargs[curArgName] = curArgVals;
	_sysargc.push_back(curArgName);
}

Core::Core(std::string localPath, int argc, char** argv)
{
	init(localPath, argc, argv);
}

void Core::init(std::string localPath, int argc, char** argv)
{
	// TODO: Parse command-line options here
	int bla = donothingspecial_simpimg();

	for(int i = 0; i < MAX_SHEETS; ++i)
		_sheets[i] = NULL;
	_fs = new Filesystem(localPath);
	_quitSignal = false;
}

int Core::createSheet(std::string masterClass)
{
	// find an empty sheet spot
	for(int i = 0; i < MAX_SHEETS; ++i)
	{
		if(_sheets[i] == NULL)
		{
			_sheets[i] = new Sheet(this, i, masterClass);
			// create master if one given
			if(masterClass != "")
			{
				AnyAOPtr tempMaster = _sheets[i]->createTopLevelObj(masterClass, "master");
				if(*tempMaster == NULL)
					LOG(logERROR) << "Error creating master [" << masterClass << "]";
				else
					tempMaster->init();
			}
			for(unsigned int j = 0; j < _callbacks.size(); ++j)
			{
				if(_callbacks[j])
					_callbacks[j]->sheetCreated(_sheets[i], i);
				else
					LOG(logWARNING) << "Null SheetCallback (" << j << ")!";
			}
			return i;
		}
	}

	LOG(logERROR) << "Cannot create sheet; no free sheet slots.";
	return -1;
}

bool Core::deleteSheet(int sheetId)
{
	if(_sheets[sheetId] != NULL)
	{
		for(unsigned int i = 0; i < _callbacks.size(); ++i)
		{
			if(_callbacks[i])
				_callbacks[i]->sheetDeleted(_sheets[sheetId], sheetId);
			else
				LOG(logWARNING) << "Null SheetCallback (" << i << ")!";
		}
		delete _sheets[sheetId];
		_sheets[sheetId] = NULL;
		return true;
	}
	else
	{
		LOG(logERROR) << "Cannot delete sheet id " << sheetId << "; sheet does not exist.";
		return false;
	}
}

Sheet* Core::getSheet(int sheetId)
{
	return _sheets[sheetId];
}

void Core::updateAllSheets(double dt)
{
	for(int i = 0; i < MAX_SHEETS; ++i)
		if(_sheets[i])
			_sheets[i]->update(dt);
}

void Core::updateSheet(int sheetId, double dt)
{
	if(_sheets[sheetId])
		_sheets[sheetId]->update(dt);
	else
		LOG(logERROR) << "Cannot update sheet " << sheetId << "; no such sheet.";
}

void Core::updateGlobalObjects(double dt)
{
	for(unsigned int i = 0; i < _gObjList.size(); ++i)
		_gObjList[i]->updateFromUM(dt);
}

void Core::sendGlobalConMessage(std::string& msg)
{
	LOG(logDEBUG) << "Sending global message!";

	for(unsigned int i = 0; i < _globalConsoles.size(); ++i)
		if(_globalConsoles[i] != NULL)
			_globalConsoles[i]->sendMessage(msg);
}

bool Core::quitSignal()
{
	return _quitSignal;
}

void Core::quit()
{
	_quitSignal = true;
}

std::vector<std::string> Core::getSysArg(const std::string& name)
{
	if(_sysargs.count(name) > 0)
		return _sysargs[name];
	else
	{
		std::vector<std::string> ret;
		return ret;
	}
}

std::vector<std::string> Core::getSysArgContexts()
{
	return _sysargc;
}

int Core::addConsole(Console* hook, int sheet, int chan)
{
	if(sheet == -1) // global hook
	{
		if(chan != 0)
			LOG(logWARNING) << "Adding global hook (sheet=-1) with chan= " << chan << "; ignoring channel";
		_globalConsoles.push_back(hook);
		return 0;
	}
	else if(_sheets[sheet] == NULL)
	{
		LOG(logERROR) << "Cannot add console hook to sheet " << sheet << " because that sheet does not exist.";
		return -1;
	}
	else // sheet >= 0, sheet exists
	{
		return _sheets[sheet]->addConsole(hook, chan);
	}
}

void Core::removeConsole(Console* hook)
{
	// TODO: fix this
	for(unsigned int i = 0; i < _globalConsoles.size(); ++i)
		if(_globalConsoles[i] == hook)
			_globalConsoles[i] = NULL;
	for(int i = 0; i < MAX_SHEETS; ++i)
		if(_sheets[i] != NULL)
			_sheets[i]->removeConsole(hook);
}

void Core::addCallback(SheetCallback* cb)
{
	_callbacks.push_back(cb);
}

void Core::addGlobalObject(GlobalObject* go, std::string oname)
{
	if(_gObjMap.count(oname) <= 0)
	{
		_gObjMap[oname] = go;
		_gObjList.push_back(go);
	}
	else
		LOG(logERROR) << "Cannot add global object with name " << oname << "; object by that name already exists.";
}

GlobalObject* Core::getGlobalObject(std::string oname)
{
	if(_gObjMap.count(oname) > 0)
		return _gObjMap[oname];
	else
	{
		LOG(logDEBUG) << "Cannot retrieve global object with name " << oname << "; no object by that name. (Probably this message is coming up because something is checking if a GO exists)";
		return NULL;
	}
}

Filesystem* Core::getFs()
{
	return _fs;
}

Core::~Core()
{
	delete _fs;
	for(int i = 0; i < MAX_SHEETS; ++i)
		if(_sheets[i])
			delete _sheets[i];
	for(unsigned int i = 0; i < _gObjList.size(); ++i)
		delete _gObjList[i];
}

/*
Sheet* _sheets[MAX_SHEETS];
std::vector<SheetCallback*> _callbacks;
std::map<std::string,GlobalObject*> _gObjMap;
Filesystem* _fs;
*/
